/*
 * Created by Jaren at 2023/1/13 13:55
 */
import 'dart:async';

import 'package:inmotion_protocol/base/file/file_manager.dart';
import 'package:inmotion_protocol/base/protocol.dart';
import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/fw_update_manager.dart';
import 'package:inmotion_protocol/common/mode.dart';
import 'package:inmotion_protocol/ezcan/ezcan_message.dart';
import 'package:inmotion_protocol/ezcan/file/ezcan_file_manager.dart';
import 'package:inmotion_protocol/protocol_worker.dart';

class EzCANFwUpdateManager extends FwUpdateManager {
  final Protocol<EzcanMessage> _protocol;
  EzCANFileManager? _fileManager;

  SingleDeviceUpdateConfig? _singleDeviceUpdateConfig;
  final LinkLayerDeviceHandler _linkLayerDeviceHandler;

  final RequiredManualOperationHandler _requiredManualOperationHandler;

  StreamController<FwUpdateEvent> fwUpdateStreamController = StreamController();

  EzCANFwUpdateManager(this._protocol, this._linkLayerDeviceHandler,
      this._requiredManualOperationHandler);

  @override
  void cancel() {}

  @override
  void config(MultiDeviceUpdateConfig config) {
    if (config.singleConfigs.isEmpty)
      throw InvalidParamException("Need 1 single config at least.");
    if (_fileManager != null) {
      _fileManager!.interrupt();
      _fileManager!.destroy();
    }
    _singleDeviceUpdateConfig =
        config.singleConfigs[config.singleConfigs.keys.first];
    _fileManager = EzCANFileManager(_protocol,
        EzcanConfig.byFwUpdate(_singleDeviceUpdateConfig!.localFile));
  }

  @override
  void destroy() {
    fwUpdateStreamController.close();
    if (_fileManager != null) _fileManager!.destroy();
  }

  Future<bool> checkModeForUpdate() {
    if (_protocol.productProperty.model.isScooter()) return Future.value(true);
    return _protocol.requestRealTimeState().then((state) {
      return state.pcMode != Mode.working;
    });
  }

  @override
  Stream<FwUpdateEvent> start() {
    if (_fileManager == null)
      throw NullCheckException(
          'FileManager is null. Please call config() first.');
    if (_singleDeviceUpdateConfig == null)
      throw NullCheckException(
          'SingleDeviceUpdateConfig is null. Please call config() first.');
    if (fwUpdateStreamController.isClosed) {
      fwUpdateStreamController = StreamController();
    }

    fwUpdateStreamController
        .add(FwUpdateEvent.byPreparing(_singleDeviceUpdateConfig!.index));

    checkModeForUpdate().then((canUpdate) {
      if (!canUpdate) {
        fwUpdateStreamController.addError(
            FwUpdateError.byStartUpdate(_singleDeviceUpdateConfig!.device, 3));
        return;
      }
      _fileManager!.start().listen((fileEvent) {
        print("757--857: ${fileEvent.type}");
        if (fileEvent.type == FileEvent.fileProgress) {
          print("757--857: ${fileEvent.type}");
          // 文件传输进度
          fwUpdateStreamController.add(FwUpdateFileTransferringEvent(
              _singleDeviceUpdateConfig!.index,
              fileEvent as FileProgressEvent));
        } else if (fileEvent.type == FileEvent.fileClosed) {
          // 文件关闭，等到设备升级
          fwUpdateStreamController.add(
              FwUpdateEvent.byWaiting4Update(_singleDeviceUpdateConfig!.index));

          // 延时后查询升级是否成功
          waitThenCheckUpdateSuccess(
              _singleDeviceUpdateConfig!.device,
              _protocol,
              fwUpdateStreamController,
              _singleDeviceUpdateConfig!,
              _linkLayerDeviceHandler,
              _requiredManualOperationHandler);
        } else {
          // 忽略其余文件操作事件
        }
      }).onError((e) {
        print("757--857: 11 $e");
        if (e is FileOperationStepError) {
          fwUpdateStreamController.addError(FwUpdateError.byFileOperation(
              _singleDeviceUpdateConfig!.device, e));
          return;
        }
        fwUpdateStreamController.addError(e);
      });
    }).onError((error, stackTrace) {
      print("757--857: 22 $error");
      if (error is MsgTimeoutException) {
        fwUpdateStreamController.addError(FwUpdateError.byStartUpdate(
            _singleDeviceUpdateConfig!.device, FwUpdateError.errorTimeout));
      }
    });

    return fwUpdateStreamController.stream;
  }
}
